package com.tempjob.app.controller;

import com.alibaba.fastjson.JSON;
import com.tempjob.app.dto.GetUserWechatPhoneDTO;
import com.tempjob.app.dto.MiniAppBindMobileDTO;
import com.tempjob.app.dto.WechatUnionIdDTO;
import com.tempjob.app.service.TokenService;
import com.tempjob.app.vo.LoginVO;
import com.tempjob.app.vo.MemberOpenIdVO;
import com.tempjob.app.vo.wechat.*;
import com.tempjob.common.businesss.mongo.entity.UserInfo;
import com.tempjob.common.businesss.service.MongoBeanUtil;
import com.tempjob.common.context.UserContextManage;
import com.tempjob.common.exception.BusinessException;
import com.tempjob.common.utils.AES;
import com.tempjob.common.utils.CommFunction;
import com.tempjob.common.utils.HttpUtils;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@Slf4j
@RestController
@RequestMapping("/api/wechat")
@CrossOrigin(originPatterns = "*", methods = {RequestMethod.GET, RequestMethod.POST})
public class WechatController {

    @Autowired
    private TokenService tokenService;

    @Value("${wechat.app.id:wx1e5520ea9486f361}")
    private String wechatAppId;

    @Value("${wechat.app.key:905045c7314f1624af5635c909170d62}")
    private String wechatAppSecret;

    private String getPhoneUrl = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=%s";

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private MongoBeanUtil mongoBeanUtil;

    @ApiOperation(response = MemberOpenIdVO.class, value = "静默登录", notes = "静默登录")
    @RequestMapping(value = "/login/{code}", method = RequestMethod.GET)
    public Object login(@PathVariable String code, HttpServletRequest request) {
        MiniAppOpenIdResp miniAppOpenIdResp = this.getMiniAppOpenIdInfo(code);
        String openid = miniAppOpenIdResp.getOpenid();
        LoginVO respVO = new LoginVO();
        respVO.setOpenId(openid);
        UserInfo user = mongoBeanUtil.getUserInfoService().lambdaQuery().eq(UserInfo::getOpenId, openid).one();
        if (null == user) {
            return respVO;
//            throw new BusinessException("用户名不存在");
        }
        String token = tokenService.createTicket(user.getId(), null);
        respVO.setAuthorization(token);
        respVO.setMobile(user.getMobile());
        user.setLoginTimes(user.getLoginTimes() == null ? 1 : user.getLoginTimes() + 1);
        user.setLastLoginTime(new Date());
        user.setLastLoginIp(CommFunction.getRealIP(request));
        mongoBeanUtil.getUserInfoService().updateById(user);
        return respVO;
    }

    @PostMapping("/logout")
    @ApiOperation(value = "退出登录", notes = "退出登录", httpMethod = "GET", produces = "application/json", response = Boolean.class)
    public Object logout() {
        tokenService.logout(UserContextManage.getAuthorization());
        return true;
    }

    @ApiOperation(response = Boolean.class, value = "获取unionId", notes = "获取unionId")
    @RequestMapping(value = "/getUnionId", method = RequestMethod.POST)
    public Object getUnionId(@RequestBody @Validated WechatUnionIdDTO dto, HttpServletRequest request) {
        MiniAppOpenIdResp miniAppOpenIdResp = this.getMiniAppOpenIdInfo(dto.getCode());
        String openid = miniAppOpenIdResp.getOpenid();
        String unionid = miniAppOpenIdResp.getUnionid();
        if (StringUtils.isEmpty(unionid)) {
            //小程序还没绑定公众平台。
            MiniAppDecodeUserInfo userWechatInfo = getUnionidByDecode(dto.getEncryptedData(), dto.getIv(), miniAppOpenIdResp.getSession_key(), wechatAppId);
            if (userWechatInfo == null || userWechatInfo.getOpenId() == null || userWechatInfo.getUnionId() == null) {
                log.error("微信信息解析失败：{}，res:{}", JSON.toJSONString(dto), JSON.toJSONString(userWechatInfo));
                throw new BusinessException("信息解析失败，请稍后重试！");
            }
            openid = userWechatInfo.getOpenId();
            unionid = userWechatInfo.getUnionId();
        }
        if (StringUtils.isEmpty(unionid)) {
            throw new BusinessException("获取unionId失败");
        }
        return unionid;
    }

    @ApiOperation(value = "微信手机号登录", notes = "微信手机号登录", response = QueryPhoneVO.class)
    @RequestMapping(value = "/mobileLogin", method = RequestMethod.POST)
    public Object mobileLogin(@RequestBody MiniAppBindMobileDTO dto, HttpServletRequest request) {
        if (StringUtils.isEmpty(dto.getPhoneCode())) {
            throw new BusinessException("请授权手机号！");
        }
        if (StringUtils.isEmpty(dto.getOpenId())) {
            throw new BusinessException("请授权！");
        }
        String accessToken = tokenService.getWechatAccessToken(false);
        if (StringUtils.isEmpty(accessToken)) {
            throw new BusinessException("获取accessToken出错！");
        }
        String openid = dto.getOpenId();
        //解密参数获取用户手机号
        String phoneUrl = String.format(getPhoneUrl, accessToken);
        String mobileReq = HttpUtils.sendBodyPost(phoneUrl, JSON.toJSONString(new GetUserWechatPhoneDTO(dto.getPhoneCode())), null);
        if (StringUtils.isEmpty(mobileReq)) {
            throw new BusinessException("手机号获取失败！");
        }
        WechatPhoneRO mobileInfo = JSON.parseObject(mobileReq, WechatPhoneRO.class);
        if (mobileInfo == null || mobileInfo.getPhone_info() == null) {
            log.info("获取用户手机号：{},{}", dto, mobileInfo);
            throw new BusinessException("获取用户手机号出错！");
        }
        String mobile = mobileInfo.getPhone_info().getPhoneNumber();
        if (StringUtils.isEmpty(mobile)) {
            throw new BusinessException("获取手机号出错！");
        }
        UserInfo sysUser = mongoBeanUtil.getUserInfoService().lambdaQuery().eq(UserInfo::getMobile, mobile).one();
        if (sysUser == null || !sysUser.getUserStatus().equals(1)) {
            throw new BusinessException("当前号码未注册，请联系客服人员!");
        }

        LoginVO respVO = new LoginVO();
        String ticket = tokenService.createTicket(sysUser.getId(), null);
        respVO.setAuthorization(ticket);
        respVO.setOpenId(openid);
        sysUser.setLoginTimes(sysUser.getLoginTimes() == null ? 1 : sysUser.getLoginTimes() + 1);
        sysUser.setLastLoginTime(new Date());
//        sysUser.setOpenId(openid);
        sysUser.setLastLoginIp(CommFunction.getRealIP(request));
        mongoBeanUtil.getUserInfoService().updateById(sysUser);
        return respVO;
    }


    /**
     * 解密隐私信息，获取unionid
     *
     * @param encryptedData
     * @param iv
     * @param session_key
     * @return
     */
    public static MiniAppDecodeUserInfo getUnionidByDecode(String encryptedData, String iv, String session_key, String appId) {
        log.info("解密unionid字符串：{},session_key:{},iv:{},appid:{}", encryptedData, session_key, iv, appId);
        try {
            byte[] resultByte = AES.decrypt(Base64.decodeBase64(encryptedData), Base64.decodeBase64(session_key), Base64.decodeBase64(iv));
            if (null != resultByte && resultByte.length > 0) {
                String decodeInfo = new String(resultByte, "UTF-8");
                MiniAppDecodeUserInfo userInfo = JSON.parseObject(decodeInfo, MiniAppDecodeUserInfo.class);
                if (StringUtils.isEmpty(userInfo.getUnionId()) || !userInfo.getWatermark().getAppid().equals(appId)) {
                    log.error("解密encryptedData失败， userInfo ：{}", decodeInfo);
                    throw new BusinessException("解密encryptedData失败");
                }
                return userInfo;
            }
        } catch (Exception e) {
            log.info("解密unionid字符串失败：{},session_key:{},iv:{},appid:{}", encryptedData, session_key, iv, appId, e);
            throw new BusinessException("微信unionid 解析失败");
        }
        return null;
    }

    /**
     * 获取openId等信息
     *
     * @param code
     * @return
     */
    String MINI_APP_OPENID_URL = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";


    private MiniAppOpenIdResp getMiniAppOpenIdInfo(String code) {
        String codeKey = "sso:miniCode:" + code;
        //先查询缓存中有没有缓存数据，没有则重新请求解密
        String info = redisTemplate.opsForValue().get(codeKey);
        if (info != null) {
            MiniAppOpenIdResp resp = JSON.parseObject(info, MiniAppOpenIdResp.class);
            log.info("getMiniAppOpenIdInfoRedis:code:{},info:{}", code, info);
            return resp;
        } else {
            String openidUrl = String.format(MINI_APP_OPENID_URL, wechatAppId, wechatAppSecret, code);
            String response = HttpUtils.sendGet(openidUrl, null,  null);
            log.info("getMiniAppOpenIdInfoWechat:code:{},info:{}", code, info);
            if (!StringUtils.isEmpty(response)) {
                try {
                    MiniAppOpenIdResp resp = JSON.parseObject(response, MiniAppOpenIdResp.class);
                    if (null != resp.getOpenid()) {
                        redisTemplate.opsForValue().set(codeKey, response, 24, TimeUnit.HOURS);
                        return resp;
                    }
                    log.error("getMiniAppOpenIdInfoOpenidNull:{},{}", openidUrl, JSON.toJSONString(response));
                } catch (Exception e) {
                    log.error("getMiniAppOpenIdInfoException:{},{}", openidUrl, JSON.toJSONString(response), e);
                }
            }
            log.error("getMiniAppOpenIdInfo:{},{}", openidUrl, JSON.toJSONString(response));
            throw new BusinessException("系统繁忙，请稍后重试");
        }
    }

    /**
     * 解密获取用户手机号
     *
     * @param encryptedData
     * @param session_key
     * @param iv
     * @return
     */
    private static String getMobileByDecode(String encryptedData, String session_key, String iv, String appId) {
        try {
            byte[] resultByte = AES.decrypt(Base64.decodeBase64(encryptedData), Base64.decodeBase64(session_key), Base64.decodeBase64(iv));
            if (null != resultByte && resultByte.length > 0) {
                String decodeInfo = new String(resultByte, "UTF-8");
                log.info("解密手机号字符串：" + decodeInfo);
                MiniAppDecodeMobile mobileInfo = JSON.parseObject(decodeInfo, MiniAppDecodeMobile.class);
                if (StringUtils.isEmpty(mobileInfo.getPhoneNumber()) || !mobileInfo.getWatermark().getAppid().equals(appId)) {
                    log.error("解密encryptedData失败， userInfo ：{}", decodeInfo);
                    throw new BusinessException("解密encryptedData失败");
                }
                return mobileInfo.getPhoneNumber();
            }
        } catch (Exception e) {
            log.error("微信手机号解析失败", e);
            throw new BusinessException("微信手机号解析失败");
        }
        return null;
    }


}
